/* 
 * File:   OperacionDescargaArchivo.cpp
 * Author: maximiliano
 * 
 * Created on 16 de junio de 2013, 00:10
 */

#include <fstream>
#include "common_Codec.h"
#include "common_NumeroUtils.h"
#include "client_OperacionDescargaArchivo.h"

using namespace std;
using namespace TP;
using namespace TP::Operaciones;

OperacionDescargaArchivo::OperacionDescargaArchivo( uint32_t pToken
                                                  , FileServiceProxy* pFileService
                                                  , Repositorio* pRepositorio
                                                  , const string &pNombreArchivo
                                                  , const uint32_t &pVersionArchivo
                                                  ) : Operacion (pToken)
{
    _repositorio = pRepositorio;
    _fileService = pFileService;
    _nombreArchivo = pNombreArchivo;
    _versionArchivo = pVersionArchivo;
}

OperacionDescargaArchivo::~OperacionDescargaArchivo() {
    
}

void OperacionDescargaArchivo::ejecutar(){
    
    // Crea el archivo en forma local.
    
    // Declaro el archvio en el repositorio.
    
    RepoItem* item = _repositorio->CrearItem();
    
    item->SetFilename(_nombreArchivo);
    item->SetVersion(_versionArchivo);

    // Declaro el archivo fisico.
    
    if (_fileUtils.CrearArchivo (item->GetRutaCompletaArchivo()))
    {
        // Declaro el archivo en el repositorio.
        _repositorio->AgregarRepoItem(item);

        // Inicia la descarga del archivo.
        this->getChunkDelServidor (1);
    }
    else
    {
        cerr << "No se peude crear el archivo " << _nombreArchivo << " en el repositorio local." << endl;
        delete item;
        this->setFinalizada(true);
    }
}

void OperacionDescargaArchivo::procesarRespuesta (vector<string> &pRespuesta){
    
    /* Formato respuesta:
     * <token>;<recurso>;<accion>;<resultado>;<args>
     */
    
    if (FileServiceConstants::GET_CHUNK == pRespuesta[2])
    {
        this->manejarRespuestaGetChunkDelServidor(pRespuesta);
    }
    
}

void OperacionDescargaArchivo::getChunkDelServidor(const uint32_t &pNumeroChunk){
    
    _fileService->GetFileChunk(getId(), _nombreArchivo, pNumeroChunk);
    
}

void OperacionDescargaArchivo::manejarRespuestaGetChunkDelServidor (vector<string> &pRespuesta){
    
    /* El archivo existe y el hash solicitado corresponde con el archivo
     * indicado.  se comunica el chunk.
     * 
     * Formato de la respuesta:
     * 
     * <token>;<usuario>;<recurso>;<accion>;<resultado>;
     *      <nombre_archivo>
     * ;    <numero_chunk>
     * ;    <hash>
     * ;    <chunk>
     * 
     */
    
    try
    {
        if (FileServiceConstants::ERROR == pRespuesta[4])
        {
            throw pRespuesta[5];
        }        

        uint32_t numeroChunk = NumeroUtils::ParseUInt32(pRespuesta[6]);
        
        if (0 != numeroChunk)
        {
            // Guardo el chunk y actualizo le repositorio.
            RepoItem *item = _repositorio->GetRepoItemByFilename(_nombreArchivo);

            this->GuardarChunkEnArchivo (item->GetRutaCompletaArchivo(), pRespuesta[7]);
            item->AddHash(pRespuesta[6]);
            
            // inicio la descarga del siguiente chunk.
            
            this->getChunkDelServidor(++numeroChunk);
        }
        else
        {
            // Finalizo la descarga del archivo.  Luego, se finalizo la tarea.
            this->setFinalizada(true);
        }
    }
    catch (const string &ex)
    {
        cerr << pRespuesta[4] << endl;
        this->setFinalizada(true);
    }
}

void OperacionDescargaArchivo::GuardarChunkEnArchivo (const string& pRutaCompletaArchivo, const string &pChunk){

    // Decodifico el chunk.
    Codec codec;
    uint8_t* chunk = NULL;
    size_t longitudChunk = 0;
    
    codec.decodificar(chunk, longitudChunk, pChunk);
    
    // Guardo el chunk en el archivo indicado.
    if (longitudChunk > 0)
    {
        ofstream file (pRutaCompletaArchivo.c_str(), ios::ate);
        if (file.is_open())
        {
            file.write((char*) chunk, longitudChunk);
        }
    }
}
